'''ex_1'''

'''
start
alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
shift = int(input("Enter the shift value: "))
message = input("Enter the message to encrypt: ")
shifted_alphabet = alphabet[shift:] + alphabet[:shift]
table = str.maketrans(alphabet, shifted_alphabet)
encrypted = message.translate(table)
end
'''

def caesar_cipher(text, shift):
    alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    shifted_alphabet = alphabet[shift:] + alphabet[:shift]
    table = str.maketrans(alphabet, shifted_alphabet)
    return text.translate(table)

'''ex_1 test'''

# Test the function
message = "HELLO WORLD"
shift = 3
encrypted = caesar_cipher(message, shift)
print(f"Original: {message}")
print(f"Encrypted: {encrypted}")

print("-" * 100)

################################################################################

'''ex_2'''

def vigenere_cipher(message, key, decrypt=False):
    alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    key_index = 0
    result = ""

    key_length = len(key) # Ensure the key is as long as the message
    message_length = len(message)
    if key_length < message_length:
        key = (key * (message_length // key_length + 1))[:message_length]
    
    
    for char in message:
        if char in alphabet:
            char_pos = alphabet.find(char)
            key_shift = alphabet.find(key[key_index])
            
            if decrypt:
                result += alphabet[(char_pos - key_shift) % len(alphabet)]
            else:
                result += alphabet[(char_pos + key_shift) % len(alphabet)]
            
            key_index = (key_index + 1) % key_length
        else:
            # Non-alphabet characters are added unchanged
            result += char
            
    return result

'''
start
alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
key_index = 0
result = ""

key_length = len(key)
message_length = len(message)
if key_length < message_length:
    key = (key * (message_length // key_length + 1))[:message_length]

for char in message:
    if char in alphabet:
        char_pos = alphabet.find(char)
        key_shift = alphabet.find(key[key_index])
        
        if decrypt:
            result += alphabet[(char_pos - key_shift) % len(alphabet)]
        else:
            result += alphabet[(char_pos + key_shift) % len(alphabet)]
        
        key_index = (key_index + 1) % key_length
        else:
            result += alphabet[(char_pos + key_shift) % len(alphabet)]
        
        key_index = (key_index + 1) % key_length
    else:
        result += char
return result
end
'''


'''ex_2 test'''

# Test the function
message = "HELLO WORLD"
keyword = "KEY"
encrypted = vigenere_cipher(message, keyword)
decrypted = vigenere_cipher(encrypted, keyword, decrypt=True)
print(f"Original: {message}")
print(f"Encrypted: {encrypted}")
print(f"Decrypted: {decrypted}")

print("-" * 100)

'''ex_3'''

'''
start

# caesar_cipher_function

alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
shift = int(input("Enter the shift value: "))
message = input("Enter the message to encrypt: ")
shifted_alphabet = alphabet[shift:] + alphabet[:shift]
table = str.maketrans(alphabet, shifted_alphabet)
encrypted = message.translate(table)

encrypted_item = self.encrypt(item)
index = self.hash_func(encrypted_item)
if not self.data[index]:
    self.data[index] = []
self.data[index].append(encrypted_item)

caesar_cipher(item, 3)
    
encrypted_item = self.encrypt(item)
index = self.hash_func(encrypted_item)
if not self.data[index]:
    raise KeyError(f"{item} is not found.")
end
'''
class HashTable:
    def __init__(self, size):
        self.size = size
        self.data = [None] * size

    def hash_func(self, item):
        return sum([ord(char) for char in item]) % self.size

    def add(self, item):
        encrypted_item = self.encrypt(item)
        index = self.hash_func(encrypted_item)
        if not self.data[index]:
            self.data[index] = []
        self.data[index].append(encrypted_item)
        return index

    def encrypt(self, item):
        return caesar_cipher(item, 3)
    
    def get(self, item):
        encrypted_item = self.encrypt(item)
        index = self.hash_func(encrypted_item)
        if not self.data[index]:
            raise KeyError(f"{item} is not found.")
        return self.data[index][0]
    
# Test the implementation
hash_table = HashTable(50)
hash_table.add('password123')
hash_table.add('secret456')
print(hash_table.data)
try:
    print(hash_table.get('password123'))
    print(hash_table.get('secret456'))
except KeyError as e:
    print(e)
    print(hash_table.get('secret456'))
    print(hash_table.get('password123'))

print("-" * 100)

'''Crack the code'''

'''ex_1'''

'''
start
def caesar_cipher(text, shift):
    alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    shifted_alphabet = alphabet[shift:] + alphabet[:shift]
    table = str.maketrans(alphabet, shifted_alphabet)
    return text.translate(table)

def brute_force_caesar(encrypted_text):
    for shift in range(26):
        decrypted_text = caesar_cipher(encrypted_text, -shift)
        print(f"Shift: {shift}, Decrypted Text: {decrypted_text}")

encrypted = "PBQJUDWXODWLRQV"
brute_force_caesar(encrypted)
end
'''

def brute_force_caesar(encrypted_text):
    def caesar_cipher(text, shift):
        alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
        shifted_alphabet = alphabet[shift:] + alphabet[:shift]
        table = str.maketrans(alphabet, shifted_alphabet)
        return text.translate(table)

    # Try all possible shifts
    # Print each attempt
    # Let user identify the correct decryption
    for shift in range(26):
        decrypted_text = caesar_cipher(encrypted_text, -shift)
        print(f"Shift: {shift}, Decrypted Text: {decrypted_text}")
    # Removed the unnecessary '# Test with the password' comment

encrypted = "PBQJUDWXODWLRQV"
brute_force_caesar(encrypted)

print("-" * 100)

'''ex_2'''

'''
start
def find_key(known_plaintext, known_ciphertext):
    alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    key = ""
    for i in range(len(known_plaintext)):
        plain_letter = known_plaintext[i]
        cipher_letter = known_ciphertext[i]
        shift = (alphabet.find(cipher_letter) - alphabet.find(plain_letter)) % len(alphabet)
        key += alphabet[shift]
    

    print(f"Key: {key}")
    return key
end
'''

def find_key(known_plaintext, known_ciphertext):
    alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    key = ""
    for i in range(len(known_plaintext)):
        plain_letter = known_plaintext[i]
        cipher_letter = known_ciphertext[i]
        shift = (alphabet.find(cipher_letter) - alphabet.find(plain_letter)) % len(alphabet)
        key += alphabet[shift]
    

    print(f"Key: {key}")
    return key
    # Test with

plaintext = "HELLO"
ciphertext = "KHOOR"
key = find_key(plaintext, ciphertext)

print("-" * 100)

'''ex_3'''

'''
start
def vigenere_cipher(message, key):
    alphabet = "abcdefghijklmnopqrstuvwxyz"
    key_index = 0
    result = ""

    key_length = len(key)  # Ensure the key is as long as the message
    message_length = len(message)
    if key_length < message_length:
        key = (key * (message_length // key_length + 1))[:message_length]
    
    for char in message:
        if char in alphabet:
            char_pos = alphabet.find(char)
            key_shift = alphabet.find(key[key_index])
            
            # if decrypt:
            #     result += alphabet[(char_pos - key_shift) % len(alphabet)]
            # else:
                result += alphabet[(char_pos + key_shift) % len(alphabet)]
            key_index = (key_index + 1) % key_length
        else:
            result += char
end
'''




def vigenere_cipher(message, key):
    alphabet = "abcdefghijklmnopqrstuvwxyz"
    key_index = 0
    result = ""

    key_length = len(key)  # Ensure the key is as long as the message
    message_length = len(message)
    if key_length < message_length:
        key = (key * (message_length // key_length + 1))[:message_length]
    
    for char in message:
        if char in alphabet:
            char_pos = alphabet.find(char)
            key_shift = alphabet.find(key[key_index])
            
            # if decrypt:
            #     result += alphabet[(char_pos - key_shift) % len(alphabet)]
            # else:
            #     result += alphabet[(char_pos + key_shift) % len(alphabet)]
            result += alphabet[(char_pos + key_shift) % len(alphabet)]
            key_index = (key_index + 1) % key_length
        else:
            # Non-alphabet characters are added unchanged
            result += char
            
    return result

def vigenere_decrypt(ciphertext, key):
    def shift_char(c, shift):
        return chr(((ord(c) - ord('A') - shift) % 26) + ord('A'))
    
    key_length = len(key)
    plaintext = []
    
    for i, char in enumerate(ciphertext):
        shift = ord(key[i % key_length]) - ord('A')
        decrypted_char = shift_char(char, shift)
        plaintext.append(decrypted_char)
    
    return ''.join(plaintext)


# Ciphertext
ciphertext = "zmkxhpvvkpfglile"

# Potential keywords (derived from the hint)
keywords = ["north"]

# Attempt decryption with each keyword
for key in keywords:
    print(f"Trying key: {key}")
    decrypted_message = vigenere_decrypt(ciphertext, key)
    print("Decrypted message:", decrypted_message)
    print()

'''##########################################################################'''

# def vigenere_decrypt(plaintext, ciphertext):
#     alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
#     max_key_length = min(len(plaintext), len(ciphertext))
    
#     def is_valid_key(key, plaintext, ciphertext):
#         decrypted_text = vigenere_cipher(ciphertext, key, decrypt=True)
#         return decrypted_text == plaintext
    
#     for key_length in range(1, max_key_length + 1):
#         for key in [alphabet[i:i + key_length] for i in range(len(alphabet) - key_length + 1)]:
#             if is_valid_key(key, plaintext[:key_length], ciphertext[:key_length]):
#                 return key
    
#     return None

# # 使用已知的vigenere_cipher函数


# # # 示例明文和密文
# # plaintext = "HELLO WORLD"
# # ciphertext = "MOQMZ SVYQZ"  # 这是一个假设的密文，实际情况中需要根据已知的密文替换

# # # 求解密钥
# # key = break_vigenere_cipher(plaintext, ciphertext)
# # print(f"密钥是: {key}")


# # Ciphertext
# ciphertext = "zmkxhpvvkpfglile"

# # Potential keywords (derived from the hint)
# keywords = ['north']

# # Attempt decryption with each keyword
# for key in keywords:
#     print(f"Trying key: {key}")
#     decrypted_message = vigenere_decrypt(ciphertext, key)
#     print("Decrypted message:", decrypted_message)
#     print()